home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 2 / Atari Mega Archive CD - Volume 2.iso / minix / up1510b.tgz / up1510b / src / commands / mv.c < prev    next >
C/C++ Source or Header  |  1990-07-23  |  5KB  |  204 lines

  1. /* mv - move files        Author: Adri Koppes */
  2.  
  3. /* 4/25/87 - J. Paradis        Bug fixes for directory handling
  4.  * 3/15/88 - P. Housel        More directory bug fixes
  5.  * 1/21/89 - B. Evans        Allow owner to move non-writable file.
  6.  *                Don't allow move to non-writable file.
  7.  *                Delete target file in case of moving to dir.
  8.  *                Requires making file readable before copy.
  9.  *                Fix up mode after copy (cp uses the target
  10.  *                mode, if any, and strips high bits).
  11.  *                Use adequate size for dotdot buffer.
  12.  * Jan  90 - B. Evans        The directory above /foo was calculated as
  13.  *                "" instead of "/", so "mv /bin /bin0" left
  14.  *                the old bin/. and bin/.. links in the
  15.  *                current directory or nowhere.
  16.  *
  17.  *                BUGS.
  18.  *                There are race conditions all over.
  19.  *                Error messages are not specific.
  20.  *                There are magic sizes 64 and 128 instead of
  21.  *                sizes related to PATH_MAX.
  22.  */
  23.  
  24. #include <sys/types.h>
  25. #include <sys/stat.h>
  26. #include <signal.h>
  27. #include <string.h>
  28.  
  29. struct stat st, st1;
  30.  
  31. main(argc, argv)
  32. int argc;
  33. char **argv;
  34. {
  35.   char *destdir;
  36.  
  37.   if (argc < 3) {
  38.     std_err("Usage: mv file1 file2 or mv dir1 dir2 or mv file1 file2 ... dir\n");
  39.     exit(1);
  40.   }
  41.   if (argc == 3) {
  42.     if (stat(argv[1], &st)) {
  43.         std_err("mv: ");
  44.         std_err(argv[1]);
  45.         std_err(" doesn't exist\n");
  46.         exit(1);
  47.     }
  48.     move(argv[1], argv[2]);
  49.   } else {
  50.     destdir = argv[--argc];
  51.     if (stat(destdir, &st)) {
  52.         std_err("mv: target directory ");
  53.         std_err(destdir);
  54.         std_err(" doesn't exist\n");
  55.         exit(1);
  56.     }
  57.     if ((st.st_mode & S_IFMT) != S_IFDIR) {
  58.         std_err("mv: target ");
  59.         std_err(destdir);
  60.         std_err(" not a directory\n");
  61.         exit(1);
  62.     }
  63.     while (--argc) move(*++argv, destdir);
  64.   }
  65.   exit(0);
  66. }
  67.  
  68. move(old, new)
  69. char *old, *new;
  70. {
  71.   int retval;
  72.   char name[64];
  73.   char parent[64];
  74.   char *oldbase;
  75.   int i;
  76.  
  77.   if ((oldbase = strrchr(old, '/')) == 0)
  78.     oldbase = old;
  79.   else
  80.     ++oldbase;
  81.  
  82.   /* It's too dangerous to fool with "." or ".." ! */
  83.   if ((strcmp(oldbase, ".") == 0) || (strcmp(oldbase, "..") == 0)) {
  84.     cant(old);
  85.   }
  86.  
  87.   /* Don't move a non-existent file. */
  88.   if (stat(old, &st) != 0) cant(old);
  89.  
  90.   /* If source is not writable, don't move it unless user owns it. */
  91.   if (access(old, 2) != 0 && st.st_uid != getuid()) cant(old);
  92.  
  93.   /* If target is a directory, form its full name. The error of moving
  94.    * a directory to itself is caught later. */
  95.   if (stat(new, &st1) == 0 && (st1.st_mode & S_IFMT) == S_IFDIR) {
  96.     if ((strlen(oldbase) + strlen(new) + 2) > 64) cant(old);
  97.     strcpy(name, new);
  98.     strcat(name, "/");
  99.     strcat(name, oldbase);
  100.     new = name;
  101.   }
  102.  
  103.   /* If target exists, do various overwriting checks. */
  104.   if (stat(new, &st1) == 0) {
  105.  
  106.     /* Don't move a file to itself. */
  107.     if (st.st_dev == st1.st_dev && st.st_ino == st1.st_ino) cant(old);
  108.  
  109.     /* Don't move on top of a directory. */
  110.     if ((st1.st_mode & S_IFMT) == S_IFDIR) cant(old);
  111.  
  112.     /* Don't move on top of a non-writable file. */
  113.     if (access(new, 2) != 0) cant(old);
  114.   }
  115.   strcpy(parent, new);
  116.  
  117.   for (i = (strlen(parent) - 1); i > 0; i--) {
  118.     if (parent[i] == '/') break;
  119.   }
  120.  
  121.   if (i == 0) {
  122.     if (parent[0] == '/')
  123.         parent[1] = '\0';    /* parent of "/bin" is "/", not "." */
  124.     else
  125.         strcpy(parent, ".");
  126.   } else {
  127.     /* Null-terminate name at last slash */
  128.     parent[i] = '\0';
  129.   }
  130.  
  131.   /* Prevent moving a directory into its own subdirectory */
  132.   if ((st.st_mode & S_IFMT) == S_IFDIR) {
  133.     char lower[128];
  134.     short int prevdev = -1;
  135.     unsigned short previno;
  136.  
  137.     strcpy(lower, parent);
  138.     while (1) {
  139.         if (stat(lower, &st1) || (st1.st_dev == st.st_dev
  140.                     && st1.st_ino == st.st_ino))
  141.             cant(old);
  142.  
  143.         /* Stop at root */
  144.         if (st1.st_dev == prevdev && st1.st_ino == previno) break;
  145.         prevdev = st1.st_dev;
  146.         previno = st1.st_ino;
  147.         strcat(lower, "/..");
  148.     }
  149.   }
  150.  
  151.   /* If target exists, it is a file. Delete it so that following link()
  152.    * works except across file systems, to avoid copying. */
  153.   if (stat(new, &st1) == 0) unlink(new);
  154.  
  155.   if (link(old, new))
  156.     if ((st.st_mode & S_IFMT) != S_IFDIR) {
  157.         switch (fork()) {
  158.             case 0:
  159.             if (!(st.st_mode & S_IRUSR))
  160.                 chmod(old, st.st_mode | S_IRUSR);
  161.             setgid(getgid());
  162.             setuid(getuid());
  163.             execl("/bin/cp", "cp", old, new, (char *) 0);
  164.             execl("/usr/bin/cp", "cp", old, new, (char *) 0);
  165.             cant(old);
  166.             case -1:
  167.             std_err("mv: can't fork\n");
  168.             exit(1);
  169.             default:
  170.             wait(&retval);
  171.             if (!(st.st_mode & S_IRUSR)) chmod(old, st.st_mode);
  172.             if (retval) cant(old);
  173.             chmod(new, st.st_mode);
  174.         }
  175.     } else
  176.         cant(old);
  177.  
  178.   /* If this was a directory that we moved, then we have * to update
  179.    * its ".." entry (in case it was moved some- * where else in the
  180.    * tree...) */
  181.   if ((st.st_mode & S_IFMT) == S_IFDIR) {
  182.     char dotdot[64 + 3];
  183.  
  184.     /* Unlink the ".." entry */
  185.     strcpy(dotdot, new);
  186.     strcat(dotdot, "/..");
  187.     unlink(dotdot);
  188.  
  189.     /* Now link it to its parent */
  190.     link(parent, dotdot);
  191.   }
  192.   utime(new, &st.st_atime);
  193.   unlink(old);
  194. }
  195.  
  196. cant(name)
  197. char *name;
  198. {
  199.   std_err("mv: can't move ");
  200.   std_err(name);
  201.   std_err("\n");
  202.   exit(1);
  203. }
  204.